package com.bsdsma.visonManagementSystemapp.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bsdsma.visonManagementSystemapp.dto.PageRetDto;
import com.bsdsma.visonManagementSystemapp.dto.SchoolDto;
import com.bsdsma.visonManagementSystemapp.dto.DataGroupByDateDto;
import com.bsdsma.visonManagementSystemapp.entity.School;
import com.bsdsma.visonManagementSystemapp.entity.Gonggao;
import com.bsdsma.visonManagementSystemapp.entity.Student;
import com.bsdsma.visonManagementSystemapp.entity.Teacher;
import com.bsdsma.visonManagementSystemapp.mapper.SchoolMapper;
import com.bsdsma.visonManagementSystemapp.mapper.GonggaoMapper;
import com.bsdsma.visonManagementSystemapp.mapper.StudentMapper;
import com.bsdsma.visonManagementSystemapp.mapper.TeacherMapper;
import com.bsdsma.visonManagementSystemapp.service.SchoolService;
import com.bsdsma.visonManagementSystemapp.utils.CustomerBeanUtils;
import com.bsdsma.visonManagementSystemapp.utils.DateUtils;
import com.bsdsma.visonManagementSystemapp.exceptionhandler.GlobalRuntimeException;
import com.bsdsma.visonManagementSystemapp.constants.ErrorCodes;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.time.LocalDate;
import java.time.ZoneId;

@Slf4j
@Service
public class SchoolServiceImpl extends ServiceImpl<SchoolMapper, School> implements SchoolService {

    private final GonggaoMapper gonggaoMapper;
    private final StudentMapper studentMapper;
    private final TeacherMapper teacherMapper;

    public SchoolServiceImpl(GonggaoMapper gonggaoMapper, StudentMapper studentMapper, TeacherMapper teacherMapper) {
        this.gonggaoMapper = gonggaoMapper;
        this.studentMapper = studentMapper;
        this.teacherMapper = teacherMapper;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public School add(SchoolDto schoolDto) {
        // Validate input
        if (StringUtils.isBlank(schoolDto.getName())) {
            throw new IllegalArgumentException("School name cannot be empty");
        }
        if (StringUtils.isBlank(schoolDto.getAddress())) {
            throw new IllegalArgumentException("School address cannot be empty");
        }

        // Check if school with same name exists
        LambdaQueryWrapper<School> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(School::getName, schoolDto.getName());
        if (this.count(queryWrapper) > 0) {
            throw new IllegalArgumentException("School with this name already exists");
        }

        // Create new school
        School school = new School();
        CustomerBeanUtils.copyProperties(schoolDto, school);
        school.setStatus(0); // Active status
        school.setCt(DateUtils.getNow());
        school.setUt(DateUtils.getNow());
        this.save(school);
        return school;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public School update(Long id, SchoolDto schoolDto) {
        // Validate input
        if (id == null) {
            throw new IllegalArgumentException("School ID cannot be null");
        }
        if (StringUtils.isBlank(schoolDto.getName())) {
            throw new IllegalArgumentException("School name cannot be empty");
        }
        if (StringUtils.isBlank(schoolDto.getAddress())) {
            throw new IllegalArgumentException("School address cannot be empty");
        }

        // Check if school exists
        School existingSchool = this.getById(id);
        if (existingSchool == null) {
            throw new IllegalArgumentException("School not found");
        }

        // Check if another school with same name exists
        LambdaQueryWrapper<School> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(School::getName, schoolDto.getName())
                   .ne(School::getId, id);
        if (this.count(queryWrapper) > 0) {
            throw new IllegalArgumentException("另外一所学校已经用了这个名字。");
        }

        // Update school
        CustomerBeanUtils.copyProperties(schoolDto, existingSchool);
        this.updateById(existingSchool);
        return existingSchool;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delete(Long id) {
        School bean = this.getById(id);
        if (bean == null) {
            throw new GlobalRuntimeException(ErrorCodes.OBJ_NOT_EXISTS, "学校不存在");
        }

        // Check for active announcements
        LambdaQueryWrapper<Gonggao> gonggaoWrapper = new LambdaQueryWrapper<>();
        gonggaoWrapper.eq(Gonggao::getXuexiaoId, id)
                     .eq(Gonggao::getStatus, 0);
        long activeGonggaoCount = gonggaoMapper.selectCount(gonggaoWrapper);
        if (activeGonggaoCount > 0) {
            throw new GlobalRuntimeException(ErrorCodes.PARAMS_ERROR, "该学校还有" + activeGonggaoCount + "条未删除的公告，无法删除");
        }

        // Check for active students
        LambdaQueryWrapper<Student> studentWrapper = new LambdaQueryWrapper<>();
        studentWrapper.eq(Student::getXuexiaoId, id)
                     .eq(Student::getStatus, 0);
        long activeStudentCount = studentMapper.selectCount(studentWrapper);
        if (activeStudentCount > 0) {
            throw new GlobalRuntimeException(ErrorCodes.PARAMS_ERROR, "该学校还有" + activeStudentCount + "名在读学生，无法删除");
        }

        // Check for active teachers
        LambdaQueryWrapper<Teacher> teacherWrapper = new LambdaQueryWrapper<>();
        teacherWrapper.eq(Teacher::getXuexiaoId, id)
                     .eq(Teacher::getStatus, 0);
        long activeTeacherCount = teacherMapper.selectCount(teacherWrapper);
        if (activeTeacherCount > 0) {
            throw new GlobalRuntimeException(ErrorCodes.PARAMS_ERROR, "该学校还有" + activeTeacherCount + "名在职教师，无法删除");
        }

        bean.setStatus(-1);
        bean.setUt(DateUtils.getNow());
        this.updateById(bean);
    }

    @Override
    public PageRetDto<SchoolDto> search(SchoolDto schoolDto, Integer pageNum, Integer pageSize) {
        // Create query wrapper
        LambdaQueryWrapper<School> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(School::getStatus, 0); // Only active schools

        // Add search criteria if provided
        if (schoolDto != null) {
            if (StringUtils.isNotBlank(schoolDto.getName())) {
                queryWrapper.like(School::getName, schoolDto.getName());
            }
            if (StringUtils.isNotBlank(schoolDto.getAddress())) {
                queryWrapper.like(School::getAddress, schoolDto.getAddress());
            }
        }

        // Execute paginated query
        Page<School> page = new Page<>(pageNum, pageSize);
        Page<School> resultPage = this.page(page, queryWrapper);

        // Convert to DTOs
        List<SchoolDto> schoolDtos = resultPage.getRecords().stream()
                .map(school -> {
                    SchoolDto dto = new SchoolDto();
                    CustomerBeanUtils.copyProperties(school, dto);
                    return dto;
                })
                .collect(Collectors.toList());

        // Create and return page result
        PageRetDto<SchoolDto> pageRetDto = new PageRetDto<>();
        pageRetDto.setContent(schoolDtos);
        pageRetDto.setTotalElements(resultPage.getTotal());
        pageRetDto.setHasNextPage(resultPage.hasNext());
        pageRetDto.setPages(resultPage.getPages());
        return pageRetDto;
    }

    @Override
    public School getById(Long id) {
        if (id == null) {
            throw new IllegalArgumentException("School ID cannot be null");
        }

        School school = baseMapper.selectById(id);
        if (school == null) {
            return null;
        }

        return school;
    }

    @Override
    public School findByName(String name) {
        if (StringUtils.isBlank(name)) {
            log.info("School name is blank");
            return null;
        }

        log.info("Searching for school with name: [{}]", name);
        
        // First, let's check what's actually in the database
        List<School> allSchools = this.list();
        log.info("All schools in database: {}", allSchools);
        
        // Try with raw SQL, with status check
        LambdaQueryWrapper<School> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(School::getName, name)
                   .eq(School::getStatus, 0)  // Only active schools
                   .last("LIMIT 1");
        
        log.info("Generated SQL: {}", queryWrapper.getSqlSegment());
        log.info("SQL parameters: {}", queryWrapper.getParamNameValuePairs());
        
        School school = this.getOne(queryWrapper);
        log.info("Found school: {}", school);
        return school;
    }

    @Override
    public List<School> listAll() {
        LambdaQueryWrapper<School> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(School::getStatus, 0);
        return this.list(queryWrapper);
    }

    @Override
    public List<DataGroupByDateDto> countByDay(Date beginDate, Date endDate) {
        if (beginDate == null || endDate == null) {
            return new ArrayList<>();
        }

        LambdaQueryWrapper<School> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(School::getStatus, 0)
                   .between(School::getCt, beginDate, endDate);
        
        List<School> schools = this.list(queryWrapper);
        Map<LocalDate, Long> countByDate = schools.stream()
            .collect(Collectors.groupingBy(
                school -> school.getCt().toInstant()
                    .atZone(ZoneId.systemDefault())
                    .toLocalDate(),
                Collectors.counting()
            ));

        return countByDate.entrySet().stream()
            .map(entry -> {
                DataGroupByDateDto dto = new DataGroupByDateDto();
                dto.setDay(Date.from(entry.getKey()
                    .atStartOfDay(ZoneId.systemDefault())
                    .toInstant()));
                dto.setNum(entry.getValue().intValue());
                return dto;
            })
            .sorted(Comparator.comparing(DataGroupByDateDto::getDay))
            .collect(Collectors.toList());
    }
} 